package models

import (
	"errors"
	"strconv"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"gorm.io/datatypes"
	"gorm.io/gorm"

	"ssalarm/common/constant"
	cDto "ssalarm/common/dto"
	"ssalarm/common/utils"
	"ssalarm/internal/alarm/models/query"
)

type SysAlarmConfig struct {
	DevSn        string          `json:"devSn" gorm:"type:varchar(255);comment:设备序列号"`
	Key          string          `json:"key" gorm:"type:varchar(255);comment:变量key"`
	Warning      datatypes.JSON  `json:"warning" gorm:"type:jsonb;comment:告警配置"`
	Enable       bool            `json:"enable" gorm:"type:jsonb;comment:启用禁用"`
	ProductCode  string          `json:"productCode" gorm:"type:varchar(255);comment:产品编码"`
	DevProductId string          `json:"devProductId" gorm:"type:varchar(255);comment:产品Id"`
	CreateTime   *cDto.LocalTime `json:"createTime" gorm:"type:timestamptz(6);comment:创建时间"`
	UpdateTime   *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:更新时间"`

	model.Model

	DevName     string `json:"devName" gorm:"-"`
	ProductName string `json:"productName" gorm:"-"`
	KeyName     string `json:"keyName" gorm:"-"`
}

func (SysAlarmConfig) TableName() string {
	return "sys_alarm_config"
}

func (e *SysAlarmConfig) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysAlarmConfig) FindAlarmConfig(req *query.SysDevAlarmConfigQuery) (err error, list []SysAlarmConfig) {
	err = e.FindList(&list,
		cDto.MakeCondition(req.GetNeedSearch()),
		cDto.OrderDest("create_time", true, req.OrderBy),
	)
	return
}

func (e *SysAlarmConfig) FindAlarmConfigList(req *query.SysAlarmConfigQuery) (err error, result interface{}) {
	// 空结果 置顶
	list := make([]SysAlarmConfig, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		result = list
	}
	// 处理查询条件
	search := req.GetNeedSearch()
	if len(search.ProdOrDev) == 0 {
		search.ProdOrDev = "device"
	}
	querySnIn := search.SnIn
	// 用户分组权限
	var snList []string
	snList, err = getConfigSnList(e.Orm, search)
	if len(snList) == 0 || err != nil {
		return
	}
	search.SnIn = snList
	// 权限
	if len(querySnIn) > 0 {
		// 取交集
		for i := len(search.SnIn) - 1; i >= 0; i-- {
			if !utils.StrIn(search.SnIn[i], querySnIn) {
				search.SnIn = append(search.SnIn[:i], search.SnIn[i+1:]...)
			}
		}
		if len(search.SnIn) == 0 {
			return
		}
	}
	// json数组查询条件预处理
	if err = preCond(e.Orm, &search); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = nil
		}
		return
	}
	// 扩展名称
	deviceName, productName, devKeyName := getFieldName(e.Orm, search.SnIn)
	// 查询结果
	if req.PageIndex > 0 && req.PageSize > 0 {
		err = e.FindPage(&list, &count,
			cDto.MakeCondition(search),
			cDto.OrderDest("create_time", true, req.OrderBy),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		)
		for i := range list {
			list[i].DevName = deviceName[list[i].DevSn]
			list[i].ProductName = productName[list[i].ProductCode]
			if m, ok := devKeyName[list[i].DevSn]; ok {
				list[i].KeyName = m[list[i].Key]
			}
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		for i := range list {
			list[i].DevName = deviceName[list[i].DevSn]
			list[i].ProductName = productName[list[i].ProductCode]
			if m, ok := devKeyName[list[i].DevSn]; ok {
				list[i].KeyName = m[list[i].Key]
			}
		}
		err = e.FindList(&list,
			cDto.MakeCondition(search),
			cDto.OrderDest("create_time", true, req.OrderBy),
		)
		result = list
	}
	return
}

func (e *SysAlarmConfig) DeleteAlarmConfig(snList []string) error {
	if err := e.Delete(e, cDto.MakeCondition(query.SysAlarmConfigQuery{SnIn: snList})); err != nil {
		return err
	}
	for _, v := range snList {
		_ = env.Cache.Del(constant.AlarmConfigKey + v)
	}
	return nil
}

func getConfigSnList(db *gorm.DB, search query.SysAlarmConfigQuery) (snList []string, err error) {
	snList = make([]string, 0)
	if search.ProdOrDev == "product" {
		devProduct := SysDevProduct{}
		devProduct.Orm = db
		products := make([]SysDevProduct, 0)
		if err = devProduct.FindList(&products, cDto.MakeCondition(query.SysProductQuery{DeptIdIn: search.DevDeptIdIn})); err != nil {
			return
		}
		if len(products) == 0 {
			return
		}
		for _, v := range products {
			snList = append(snList, v.ProductModel)
		}
	} else if search.ProdOrDev == "device" {
		devModel := SysDev{}
		devModel.Orm = db
		devList := make([]SysDev, 0)
		if err = devModel.FindList(&devList, cDto.MakeCondition(query.SysDevQuery{DeptIdIn: search.DevDeptIdIn})); err != nil {
			return
		}
		if len(devList) == 0 {
			return
		}
		for _, v := range devList {
			snList = append(snList, v.Sn)
		}
	}
	return
}

func preCond(db *gorm.DB, search *query.SysAlarmConfigQuery) (err error) {
	preSql := "select * from sys_alarm_config T,jsonb_array_elements( T.warning ) as h "
	needPre := search.PageIndex > 0 && search.PageSize > 0
	if needPre {
		preSql += " where jsonb_array_length(T.warning) > 0 "
	} else {
		preSql += " where h :: jsonb ->> 'alarmType' != '' "
	}
	if search.AlarmLevel != nil {
		needPre = true
		preSql += ` and h :: jsonb ->> 'warningLevel' = '` + strconv.Itoa(*search.AlarmLevel) + `'`
	}
	if len(search.AlarmType) > 0 {
		needPre = true
		preSql += ` and h :: jsonb ->> 'alarmType' like '%` + search.AlarmType + `%'`
	}
	if len(search.KeyName) > 0 {
		needPre = true
		preSql += ` and h :: jsonb ->> 'name' like '%` + search.KeyName + `%'`
	}
	if search.LimitType != nil {
		needPre = true
		switch *search.LimitType {
		case 1:
			// 越上限
			preSql += ` and h :: jsonb ->> 'warningUlimit' is not null`
		case 2:
			// 越下限
			preSql += `and h :: jsonb ->> 'warningDlimit' is not null`
		case 3:
			// 越上下限
			preSql += ` and h :: jsonb ->> 'warningUlimit' is not null and h :: jsonb ->> 'warningDlimit' is not null`
		case 4:
			// 等于
			preSql += ` and h :: jsonb ->> 'warningElimit' is not null`
		case 5:
			// 事件
			preSql += ` and h :: jsonb ->> 'warningMode' = '5' `
		}
	}
	if needPre {
		preList := make([]SysAlarmConfig, 0)
		if err = db.Raw(preSql).Scan(&preList).Error; err != nil {
			return
		}
		if len(preList) == 0 {
			err = gorm.ErrRecordNotFound
			return
		}
		snList := make([]string, 0)
		keyList := make([]string, 0)
		for _, v := range preList {
			snList = append(snList, v.DevSn)
			keyList = append(keyList, v.Key)
		}
		if len(search.SnIn) > 0 {
			// 取交集
			for i := len(search.SnIn) - 1; i >= 0; i-- {
				if !utils.StrIn(search.SnIn[i], snList) {
					search.SnIn = append(search.SnIn[:i], search.SnIn[i+1:]...)
				}
			}
		} else {
			search.SnIn = snList
		}
		search.KeyIn = keyList
	}
	return
}

func getFieldName(db *gorm.DB, snIn []string) (deviceName, productName map[string]string, devKeyName map[string]map[string]string) {
	// 设备名
	devCache, _ := env.Cache.HashGetAll(constant.DeviceEasyCache)
	deviceName = make(map[string]string)
	for sn, cachestr := range devCache {
		deviceName[sn] = utils.BytesToMap([]byte(cachestr))["name"].(string)
	}
	// 产品名
	pm := &SysDevProduct{}
	pm.Orm = db
	productName = pm.GetNameMapper()
	// 变量名
	dv := &SysDevVariable{}
	dv.Orm = db
	devKeyName = dv.GetNameMapper(snIn)
	return
}
